package com.yonyou.brigade.third_parties.didi.util;

import com.yonyou.brigade.third_parties.didi.model.DiDiConfig;
import com.yonyou.ybzboot.starter.common.exception.CustomException;
import com.yonyou.ybzboot.starter.common.util.StringUtil;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@Component
public class DiDiRMIUtils {

    @Value("${didi.host}")
    private String didiHost;

    private Logger logger = LoggerFactory.getLogger(DiDiRMIUtils.class);

    public String executeRMIRequest(String methodName,
                                    Map<String, Object> paramsMap, String requestType) throws CustomException {
        logger.debug("DiDi Request begin: ## methodName->" + methodName + "##");
        JSONObject resultJsonObject = new JSONObject();
        HttpClient client = new HttpClient();
        Protocol protocol = Protocol.getProtocol("https");
        client.getHostConfiguration().setHost(didiHost, protocol.getDefaultPort(), protocol);
        client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,
                "UTF-8");
        HttpMethod method;
        if ("POST".equals(requestType)) {
            method = new PostMethod(methodName);
        } else {
            method = new GetMethod(methodName);
        }

        try {
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            for (Object key : paramsMap.keySet()) {
                NameValuePair tempNameValuePair = new NameValuePair(
                        key.toString(), paramsMap.get(key).toString());
                list.add(tempNameValuePair);
            }
            NameValuePair[] names = list.toArray(new NameValuePair[0]);
            if ("POST".equals(requestType)) {
                JSONObject jsonObject = new JSONObject(paramsMap);
                String transJson = jsonObject.toString();
                RequestEntity se = new StringRequestEntity(transJson, "application/json", "UTF-8");

                PostMethod postMethod = (PostMethod) method;
                postMethod.setRequestEntity(se);
            } else {
                method.setQueryString(names);
            }

            client.executeMethod(method);
            resultJsonObject = DealRequestResult(method);
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
        } catch (HttpException e) {
            logger.error(e.getMessage(), e);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
        } finally {
            method.releaseConnection();
            method.abort();
        }
        logger.error("请求返回结果->" + resultJsonObject.toString());
        return resultJsonObject.toString();
    }

    private JSONObject DealRequestResult(HttpMethod method) throws IOException, JSONException, CustomException {
        InputStream in = null;
        JSONObject resultJsonObject = new JSONObject();
        ByteArrayOutputStream outputStream = null;
        try {
            int statusCode = method.getStatusCode();
            if (200 == statusCode) {
                in = method.getResponseBodyAsStream();
                outputStream = new ByteArrayOutputStream(1024);
                int readLength = 0;
                byte[] buffer = new byte[4096];
                while ((readLength = in.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, readLength);
                }
                String jsonString = outputStream.toString();
                resultJsonObject = new JSONObject(jsonString);
                logger.info("调用结果" + resultJsonObject.toString());
            } else {
                logger.error("调用发生错误，错误代码" + statusCode);
                throw new CustomException("调用发生错误，错误代码" + statusCode, method.getStatusText());
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(outputStream);
            method.releaseConnection();
            method.abort();
        }
        return resultJsonObject;
    }

    /**
     * 请使用这个方法
     *
     * @return
     * @throws CustomException
     */
    public String getToken(DiDiConfig ddConfig) throws CustomException {
        JSONObject jsonObject = getAccessToken(ddConfig);
        String tokenString = jsonObject.optString("access_token");
        return tokenString;
    }

    public String concatRequestParamsAndMd5Encode(Map<String, Object> paramsMap) {
        String signString = "";
        for (String keyString : paramsMap.keySet()) {
            if (StringUtil.isEmpty(signString)) {
                signString += keyString + "="
                        + paramsMap.get(keyString).toString().trim();
            } else {
                signString += "&" + keyString + "="
                        + paramsMap.get(keyString).toString().trim();
            }
        }
        return md5(signString);
    }

    public Map<String, Object> keySort(Map<String, Object> paramsMap, DiDiConfig ddConfig) {
        paramsMap.put("sign_key", ddConfig.getSignKey());
        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
        List<String> keyList = new ArrayList<String>();
        for (String keyString : paramsMap.keySet()) {
            keyList.add(keyString);
        }
        Collections.sort(keyList);
        for (String keyString : keyList) {
            resultMap.put(keyString, paramsMap.get(keyString));
        }
        return resultMap;
    }

    private void createXML(String fileName) {
        Document document = DocumentHelper.createDocument();
        Element root = document.addElement("ermdd");
        StringWriter strWtr = new StringWriter();
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("gb2312");
        XMLWriter xmlWriter = new XMLWriter(strWtr, format);
        try {
            xmlWriter.write(document);
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            logger.error(e1.getMessage().toString(), e1);
        }
        File file = new File(fileName);
        try {
            file.createNewFile();
            XMLWriter out = new XMLWriter(new FileWriter(file));
            out.write(document);
            out.flush();
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            logger.error(e.getMessage().toString(), e);
        }
    }

    private static Document getDocument(String filename)
            throws FileNotFoundException, DocumentException,
            UnsupportedEncodingException {
        return new SAXReader().read(new InputStreamReader(new FileInputStream(
                filename), "UTF-8"));
    }

    private String generateTokenString(String filenameString, Document document, Element tokenElement, Element tsElement, DiDiConfig ddConfig) throws CustomException, IOException {
        JSONObject jsonObject = getAccessToken(ddConfig);
        String tokenString = jsonObject.optString("access_token");
        tokenElement.setText(tokenString);
        tsElement.setText(String.valueOf(System.currentTimeMillis()));
        OutputFormat format = new OutputFormat(" ", true);
        format.setEncoding(DiDiConstants.CHAR_SET);
        XMLWriter xmlWriter = new XMLWriter(new PrintWriter(filenameString), format);
        xmlWriter.write(document);
        xmlWriter.close();
        return tokenString;
    }

    private JSONObject getAccessToken(DiDiConfig ddConfig) throws CustomException {
        String methodNameString = DiDiConstants.METHOD_POST_ACCESSTOKEN;
        Map<String, Object> tokenParamsMap = generateTokenParamsMap(ddConfig);
        String string = executeRMIRequest(methodNameString, tokenParamsMap, "POST");
        try {
            logger.info("dd token:" + string);
            return new JSONObject(string);
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            return new JSONObject();
        }
    }

    private Map<String, Object> generateTokenParamsMap(DiDiConfig ddConfig) {
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        if (ddConfig == null) {
            return null;
        }
        paramsMap.put(DiDiConstants.KEY_CLIENT_ID, ddConfig.getClientId());
        paramsMap.put(DiDiConstants.KEY_CLIENT_SECRET, ddConfig.getClientSecret());
        paramsMap.put(DiDiConstants.KEY_GRANT_TYPE, DiDiConstants.GRANT_TYPE);
        paramsMap.put(DiDiConstants.KEY_PHONE, ddConfig.getPhone());
        paramsMap.put(DiDiConstants.KEY_TIMESTAMP, System.currentTimeMillis());
        Map<String, Object> tokenParamsMap = keySort(paramsMap, ddConfig);
        String sign = concatRequestParamsAndMd5Encode(tokenParamsMap);
        tokenParamsMap.put(DiDiConstants.KEY_SIGN, sign);
        return tokenParamsMap;
    }

    private String md5(String plainText) {
        logger.error("Before MD5: " + plainText);
        byte[] secretBytes;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes(Charset.forName("UTF-8")));
        } catch (NoSuchAlgorithmException e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException("没有md5这个算法！");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        logger.error("After MD5: " + md5code);
        return md5code;
    }
}
